/**
* 
* ====================================================================
*
* @author apov <apov@ymail.com>
*/ 
// ShareMemory.h
//

#pragma once

#include "BaseDefine.h"
#include "LogService.h"
#include <string>

/**
* @class ShareMemory
*  - 
*/
template <class T>
class ShareMemory{
public:
	enum{
		DEF_SHM_CREATE_OK,
		DEF_SHM_CREATE_ALREADY,
		DEF_SHM_CREATE_FAIL
	};

	virtual int create( int size, const char* id ) = 0;
	virtual void close() = 0;

	T* get(){
		return data_;
	}

	T* get( size_t idx ){
		if( idx >= (size_t)size_ )
			return 0;

		return &(data_[idx]);
	}

	const char* getName(){ return name_.c_str(); }

	size_t getCount(){ return size_; }

	ShareMemory(){
		data_ = 0;
		size_ = 0;
	}

	virtual ~ShareMemory(){}

protected:
	bool set_valid_id( const char* id ){
		if(! id )
			return false;

		int size = (int)strlen( id ) - 1;
		int i = 0;

		for( i = size ; i >= 0 ; i-- ){
			if( isalnum( id[ i ] ) )
				continue;

			if( id[ i ] == '.' )
				continue;

			if( id[ i ] == '_' )
				continue;

			if( id[ i ] == '-' )
				continue;

			if( id[ i ] == '(' )
				continue;

			if( id[ i ] == ')' )
				continue;

			if( id[ i ] == '<' )
				continue;

			if( id[ i ] == '>' )
				continue;

			if( id[ i ] == '[' )
				continue;

			if( id[ i ] == ']' )
				continue;

			if( id[ i ] == '{' )
				continue;

			if( id[ i ] == '}' )
				continue;

			break;
		}

		if( i <= -1 ){
			name_ = id;

			return true;
		}

		std::string str = id;
		name_ = str.substr( i + 1, ( size + 1 ) - i );

		LOG_ERR( LOG_SERVICE( "instance" )->Log("shared id %s ===> %s", id, name_.c_str() ) );

		return false;
	}

	T* data_;
	int size_;
	std::string name_;
	
private:	
};

#ifdef WIN32

/**
* @class WinShareMemory
*  - 
*/
template <class T>
class DEFLIB_EXTERN_CLASS WinShareMemory : public ShareMemory<T>{
public:
	int create( int size, const char* id);
	void close();

	WinShareMemory(){
		key_ = INVALID_HANDLE_VALUE;
	}

	virtual ~WinShareMemory(){
		close();
	}

protected:
	
private:
	HANDLE key_;
};

template<class T>
inline int WinShareMemory<T>::create( int size, const char* id){
	set_valid_id( id );

	int result = DEF_SHM_CREATE_FAIL;

	if(! id )
		return DEF_SHM_CREATE_FAIL;	
	
	size_ = size;
	int create_size = sizeof(T) * size;

	key_ = CreateFileMapping(INVALID_HANDLE_VALUE, 0, PAGE_READWRITE, 0, create_size, name_.c_str() );
	if(! key_)
		return DEF_SHM_CREATE_FAIL;

	if( GetLastError() == ERROR_ALREADY_EXISTS )
		result = DEF_SHM_CREATE_ALREADY;
	else
		result = DEF_SHM_CREATE_OK;

	data_ = (T*)MapViewOfFile(key_, FILE_MAP_ALL_ACCESS, 0, 0, 0);
	return result;
}

template<class T>
void WinShareMemory<T>::close(){
	if( data_ ){
		UnmapViewOfFile(data_);	
		data_ = 0;
	}

	if( key_ ){
		CloseHandle(key_);
		key_ = 0;
	}
}

#endif

#ifdef LINUX

#include "ShareMemory.h"
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

/**
* @class UnixShareMemory
*  - 
*/
template <class T>
class UnixShareMemory : public ShareMemory<T>{
public:
	int create( int size, const char* id ){
		bool b = ShareMemory<T>::set_valid_id( id );

		if(! id)
			return ShareMemory<T>::DEF_SHM_CREATE_FAIL;

		int create_size = sizeof(T) * size;
		ShareMemory<T>::size_ = size;

		key_ = ftok(".", id[0]);
		if((id_ = shmget( key_, ShareMemory<T>::size_, IPC_CREAT | IPC_EXCL | 0666)) == -1){
			if((id_ = shmget(key_, ShareMemory<T>::size_, 0)) == -1)
				return ShareMemory<T>::DEF_SHM_CREATE_FAIL;
			else 
				return ShareMemory<T>::DEF_SHM_CREATE_ALREADY;
		}

		ShareMemory<T>::data_ = (T*)shmat(id_, 0, 0);

		//err edit
		if(! ShareMemory<T>::data_ )
			return ShareMemory<T>::DEF_SHM_CREATE_FAIL;

		return ShareMemory<T>::DEF_SHM_CREATE_OK;
	}

	void close(){
		if( ( id_ != 0 ) && ( key_ != 0 ) )
			shmctl(id_, IPC_RMID, NULL);

		key_ = 0;
		id_ = 0;
	}

	UnixShareMemory(){
		key_ = 0;
		id_ = 0;
	}

	virtual ~UnixShareMemory(){
		close();
	}

protected:
	
private:
	int key_;
	int id_;	
};

#endif
